feat(joke-bot): conversational test bot + gate Slack wakes on @mention#83
Conversation
joke-bot: a minimal conversational agent (DM via relay / @mention in Slack → claude/codex/opencode harness reply). Purpose: confirm the conversational + multi-turn-threading path works in isolation (no provider data), and serve as the harness test vehicle. linear-slack: gate the slack `message.created` trigger with `match: '@mention'`. Previously it woke (and provisioned a Daytona box + ran the harness) on EVERY message in the board channel, then self-filtered — a sandbox per message. The handler's skip-guards run AFTER provisioning, so only a trigger-level `match` saves the box. Now it only provisions when actually addressed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
📝 WalkthroughWalkthroughAdds a new ChangesJoke-bot persona and agent
linear-slack
Sequence Diagram(s)sequenceDiagram
participant SlackUser
participant SlackClient
participant AgentHandler
participant Memory
participant ClaudeHarness
rect rgba(70, 130, 180, 0.5)
Note over SlackUser,SlackClient: Slack `@mention` path
SlackUser->>SlackClient: `@mention` message in channel
SlackClient->>AgentHandler: Slack event (match: `@mention`)
AgentHandler->>Memory: recall thread history by ts tag
Memory-->>AgentHandler: prior turn lines
AgentHandler->>ClaudeHarness: run reply-only prompt + history
ClaudeHarness-->>AgentHandler: joke reply (exitCode 0)
AgentHandler->>SlackClient: post reply in-thread
AgentHandler->>Memory: save turn with TTL + thread tag
end
rect rgba(60, 179, 113, 0.5)
Note over AgentHandler,SlackClient: Daily cron path
AgentHandler->>ClaudeHarness: run joke-of-the-day prompt
ClaudeHarness-->>AgentHandler: joke output
AgentHandler->>SlackClient: post to SLACK_CHANNEL
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b1e460954
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return; | ||
| } | ||
|
|
||
| const convoTag = `joke-convo:${channel}`; |
There was a problem hiding this comment.
Partition relay memory by inbox conversation
When more than one person uses the relay inbox with the same SLACK_CHANNEL, this tag stores every DM turn in one workspace-scoped conversation, so a later user's prompt can include another user's prior jokes and context. The fixture already carries a relay inbox channel/message id; include the relay conversation/user identifier in the tag instead of keying only by the Slack output channel.
Useful? React with 👍 / 👎.
| const data = ((await event.expand('full').catch(() => undefined)) as { data?: Record<string, unknown> } | undefined)?.data ?? {}; | ||
| const channel = typeof data.channel === 'string' ? data.channel : undefined; | ||
| const ts = typeof data.ts === 'string' ? data.ts : undefined; |
There was a problem hiding this comment.
Unwrap Slack event payloads before reading fields
In deployments where Slack message.created arrives wrapped as resource.payload or resource.record (the shape linear-slack handles explicitly), data.channel and data.ts are undefined here, so every @mention exits through joke-bot.slack-no-target and the Slack chat path never replies. Unwrap the Slack record before reading channel, ts, text, and bot/subtype fields.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@joke-bot/agent.ts`:
- Around line 96-97: The convoTag at line 96 is constructed using the shared
Slack writeback channel instead of a unique source-conversation identity, which
allows different users to pollute or leak into each other's recalled context
when turns are saved at lines 127-129. Replace the channel-based tag
construction with a tag built from the source-conversation identity (the relay
sender or conversation ID) to ensure each user has isolated memory and prevent
cross-user context leakage.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 75005fd8-c34e-4f2b-bdcd-b263d07bf75d
📒 Files selected for processing (4)
joke-bot/agent.tsjoke-bot/fixtures/inbox-joke.jsonjoke-bot/persona.tslinear-slack/agent.ts
| const convoTag = `joke-convo:${channel}`; | ||
|
|
There was a problem hiding this comment.
Use source-conversation identity for relay memory tags to avoid cross-user leakage.
Line 96 builds convoTag from the configured Slack writeback channel, not the relay sender/conversation identity. Then Line 128 saves turns under that shared tag, so different users can pollute or leak into each other’s recalled context.
🔧 Proposed fix
- const convoTag = `joke-convo:${channel}`;
+ const relayPayload = (await event.expand('full').catch(() => undefined)) as
+ | { data?: Record<string, unknown>; resource?: Record<string, unknown> }
+ | undefined;
+ const relaySource =
+ (typeof relayPayload?.data?.channel === 'string' && relayPayload.data.channel) ||
+ (typeof relayPayload?.resource?.channel === 'string' && relayPayload.resource.channel) ||
+ (typeof relayPayload?.data?.senderId === 'string' && relayPayload.data.senderId) ||
+ 'unknown';
+ const convoTag = `joke-convo:relay:${relaySource}`;Also applies to: 127-129
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@joke-bot/agent.ts` around lines 96 - 97, The convoTag at line 96 is
constructed using the shared Slack writeback channel instead of a unique
source-conversation identity, which allows different users to pollute or leak
into each other's recalled context when turns are saved at lines 127-129.
Replace the channel-based tag construction with a tag built from the
source-conversation identity (the relay sender or conversation ID) to ensure
each user has isolated memory and prevent cross-user context leakage.
The harness path booted a full claude CLI session per reply (minutes; hit the 5-min step timeout). Switch reply generation to ctx.llm.complete — one direct inference call, subscription-backed — so replies land in ~one box-coldstart instead of timing out. Keep sandbox:true (the Slack writeback needs the relayfile mount) + useSubscription:true (so ctx.llm resolves the credential). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
ℹ️ pr-reviewer: review only — no file changes were applied to the PR (nothing to commit after review). The notes below are advisory and were not pushed. Review: PR #83 —
|
Review of PR #83 —
|
joke-bot, the linear-slack @mention gate, and the relay-helpers ^0.4.1 bump already landed on main via #83/#84. Resolve the add/add conflicts in favor of this branch (identical content for all except joke-bot/agent.ts, where this branch additionally carries the PR #85 review fixes: channel id normalization + leading-only mention strip). Net-new here remains inbox-buddy + those fixes.
joke-bot (new)
A minimal conversational agent to confirm the conversational + multi-turn-threading path works in isolation (no provider data / VFS) and to serve as the harness test vehicle (claude/codex/opencode). DM via relay or
@AgentRelay joke-bot …in Slack → harness reply in-thread. Daily joke-of-the-day on a schedule.linear-slack (fix: gate wake on @mention)
linear-slackwoke on everymessage.createdin its board channel, provisioning a Daytona box + running the harness, then self-filtered — a sandbox per message. The handler's skip-guards run after provisioning, so only a trigger-level gate saves the box.Added
match: '@mention'so the cloud only provisions when the agent is actually addressed.Why
match(notsandbox: false)Both are harness agents (
ctx.harness.run).sandbox: falseis not an option for them — with the lightweight path the harness CLI credentials aren't mounted (EMPTY_HARNESS_CLI_CREDENTIAL_MOUNT), so a real harness run can't authenticate. The wake gate (match) is the correct lever;sandbox: truestays.Companion fixes: customer-health (separate repo PR) and the
creating-cloud-personaskill doc (corrects thesandbox:falsetable + documents the sandbox-per-message trap).🤖 Generated with Claude Code
Summary by cubic
Adds a minimal conversational
joke-botfor multi-turn chat that replies in Slack via DM or@mention. Also gateslinear-slackwakes behind@mentionto avoid provisioning a sandbox on every channel message.New Features
joke-bot: conversational bot with per-thread memory; replies in Slack threads (DM or@mention).SLACK_CHANNEL.joke-bot/fixtures/inbox-joke.json.Bug Fixes
joke-bot: switch reply generation toctx.llm.complete(subscription-backed) to fix harness timeouts; keepsandbox: trueanduseSubscription: truefor Slack writeback.linear-slack: addmatch: '@mention'to themessage.createdtrigger so the cloud provisions only when addressed.Written for commit b4bd4f3. Summary will update on new commits.